package com.qijian.common.utils.mybatis;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.interfaces.Compare;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qijian.common.constant.DataScopeConstants;
import com.qijian.common.core.domain.entity.SysRole;
import com.qijian.common.core.domain.entity.SysUser;
import com.qijian.common.utils.ObjectUtils;
import com.qijian.common.utils.SecurityUtils;
import com.qijian.common.utils.StringUtils;
import com.qijian.common.utils.reflect.ReflectUtils;
import lombok.Data;

import java.lang.reflect.Field;
import java.util.*;

@Data
public class WhereEntityTool {
    private static Map<WhereTypeEnum, WhereFun> typeFunc;

    static {
        if (typeFunc == null) {
            typeFunc = new HashMap<>();
            typeFunc.put(WhereTypeEnum.EQ, Compare::eq);
            typeFunc.put(WhereTypeEnum.NEQ, Compare::ne);
            typeFunc.put(WhereTypeEnum.IN, (w, k, v) -> {
                if (ObjectUtils.isEmpty(v)) {
                    return;
                }
                if (v instanceof Collection) {
                    w.in(k, (Collection<?>) v);
                } else if (v instanceof Object[]) {
                    w.in(k, (Object[]) v);
                } else {
                    w.in(k, v.toString());
                }
            });
            typeFunc.put(WhereTypeEnum.NOTIN, (w, k, v) -> {
                if (ObjectUtils.isEmpty(v)) {
                    return;
                }
                if (v instanceof Collection) {
                    w.notIn(k, (Collection<?>) v);
                } else if (v instanceof Object[]) {
                    w.notIn(k, (Object[]) v);
                } else {
                    w.notIn(k, v.toString());
                }
            });
            typeFunc.put(WhereTypeEnum.LIKE, (w, k, v) -> w.like(k, v.toString()));
            typeFunc.put(WhereTypeEnum.LIKE_LEFT, (w, k, v) -> w.likeLeft(k, v.toString()));
            typeFunc.put(WhereTypeEnum.LIKE_RIGHT, (w, k, v) -> w.likeRight(k, v.toString()));
            typeFunc.put(WhereTypeEnum.NOT_LIKE, (w, k, v) -> w.notLike(k, v.toString()));
            typeFunc.put(WhereTypeEnum.LE, Compare::le);
            typeFunc.put(WhereTypeEnum.LT, Compare::lt);
            typeFunc.put(WhereTypeEnum.GE, Compare::ge);
            typeFunc.put(WhereTypeEnum.GT, Compare::gt);
            typeFunc.put(WhereTypeEnum.BETWEEN, (w, k, v) -> {
                if (ObjectUtils.isNull(v)) {
                    return;
                }
                if (v instanceof Object[]) {
                    Object[] data = (Object[]) v;
                    w.between(k, data[0], data[1]);
                } else if (v instanceof List) {
                    @SuppressWarnings("unchecked")
                    List<Object> data = (List<Object>) v;
                    if (CollectionUtil.isNotEmpty(data) && data.size() == 2) {
                        w.between(k, data.get(0), data.get(1));
                    }
                }
            });
            typeFunc.put(WhereTypeEnum.NULL, (w, k, v) -> {
                if (v instanceof Boolean && BooleanUtil.toBoolean(v.toString())) {
                    w.isNull(k);
                }
            });
            typeFunc.put(WhereTypeEnum.NOTNULL, (w, k, v) -> {
                if (v instanceof Boolean && BooleanUtil.toBoolean(v.toString())) {
                    w.isNotNull(k);
                }
            });
            typeFunc.put(WhereTypeEnum.JSON, (w, k, v) -> {
                if (!Objects.nonNull(v)) {
                    return;
                }
                String p = null;
                if (v instanceof Map) {
                    p = JSON.toJSONString(v);
                } else if (v instanceof String) {
                    p = (String) v;
                }
                if (p != null) {
                    w.apply("JSON_CONTAINS(" + k + ",'" + p + "')");
                }
            });
        }

    }

    /**
     * 封装成需要的wrapper
     *
     * @param t 实体对象
     * @return QueryWrapper
     */
    public static <T> QueryWrapper<T> invoke(Object t) {
        return invoke(t, true);
    }

    public static <T> QueryWrapper<T> invoke(Object t, boolean hasDel) {
        if (t == null) {
            return null;
        }
        QueryWrapper<T> wrapper = new QueryWrapper<>();
        execute(t, wrapper, hasDel);
        // 获取
        return wrapper;
    }

    public static void invoke(Object t, QueryWrapper<Object> wrapper) {
        invoke(t, wrapper, true);
    }

    public static void invoke(Object t, QueryWrapper<Object> wrapper, boolean hasDel) {
        execute(t, wrapper, hasDel);
    }

    /**
     * 执行
     *
     * @param t obj
     */
    public static <T> void execute(Object t, QueryWrapper<T> wrapper, boolean hasDel) {
        TableName topTableName = t.getClass().getAnnotation(TableName.class);
        String topTableNameVal = "";
        if (topTableName != null) {
            topTableNameVal = topTableName.value() + ".";
        }
        //默认添加is_del = 0的条件
        if (hasDel) {
            typeFunc.get(WhereTypeEnum.EQ).whereFunc(wrapper, topTableNameVal + "is_del", 0);
        }
        //反射获取属性
        Class<?> clazz = t.getClass();
        Field[] fields = ReflectUtils.getAllFieldWithSuper(clazz);
        //遍历拿出所有的条件分组信息，用or拼接
        Map<String, List<Field>> orGroup = new HashMap<>(fields.length);
        List<Field> fieldList = new ArrayList<>(Arrays.asList(fields));
        Iterator<Field> it = fieldList.iterator();
        while (it.hasNext()) {
            Field field = it.next();
            WhereType whereType = field.getAnnotation(WhereType.class);
            if (whereType == null) {
                continue;
            }
            if (StringUtils.isNotBlank(whereType.orGroup())) {
                if (!orGroup.containsKey(whereType.orGroup())) {
                    orGroup.put(whereType.orGroup(), new ArrayList<>());
                }
                try {
                    field.setAccessible(true);
                    Object val = field.get(t);
                    if (ObjectUtils.isNotEmpty(val)) {
                        orGroup.get(whereType.orGroup()).add(field);
                        it.remove();
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }
        }
        //fieldList 里是过滤掉orGroup后剩余的字段
        //fields 里依然是全量的字段
        for (Field field : fieldList) {
            try {
                field.setAccessible(true);
                String fieldName = field.getName();
                if ("serialVersionUID".equals(fieldName)) {
                    continue;
                }
                Object val = field.get(t);
                WhereType whereType = field.getAnnotation(WhereType.class);
                handle(fieldName, whereType, field, topTableNameVal, val, wrapper);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        if (CollectionUtil.isNotEmpty(orGroup)) {
            for (List<Field> value : orGroup.values()) {
                String finalTopTableNameVal = topTableNameVal;
                if (value.size() > 0) {
                    wrapper.and(q -> {
                        for (Field field : value) {
                            try {
                                String fieldName = field.getName();
                                handle(fieldName, field.getAnnotation(WhereType.class), field,
                                        finalTopTableNameVal, field.get(t), q.or());
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }
        }
    }

    private static <T> void handle(String fieldName, WhereType whereType, Field field, String topTableNameVal, Object val, QueryWrapper<T> wrapper) {
        // 数据权限
        if (fieldName.contains(DataScopeConstants.DATA_SCOPE) && ObjectUtils.isNotNull(val) && Boolean.parseBoolean(val.toString())) {
            String deptAlias = whereType.deptTableName();
            String userAlias = whereType.userTableName();
            SysUser user = SecurityUtils.getLoginUser().getUser();
            StringBuilder sqlString = new StringBuilder();

            for (SysRole role : SecurityUtils.getRoles()) {
                String dataScope = role.getDataScope();
                sqlString.append(sqlString.length() > 0 ? " OR " : "");
                if (DataScopeConstants.DATA_SCOPE_ALL.equals(dataScope)) {
                    sqlString = new StringBuilder();
                    break;
                } else if (DataScopeConstants.DATA_SCOPE_CUSTOM.equals(dataScope)) {
                    sqlString.append(StringUtils.format(
                            " {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
                            role.getRoleId()));
                } else if (DataScopeConstants.DATA_SCOPE_DEPT.equals(dataScope)) {
                    sqlString.append(StringUtils.format("{}.dept_id = {} ", deptAlias, user.getDeptId()));
                } else if (DataScopeConstants.DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
                    if ("main_process_order.".equals(topTableNameVal)) {
                        sqlString.append(StringUtils.format(
                                " {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} OR find_in_set( {} , ancestors ) )" +
                                        "OR {}factory_id in (select factory_id from fm_factory_user where user_id =  {} and is_del = 0)",
                                deptAlias, user.getDeptId(), user.getDeptId(), topTableNameVal, user.getUserId()));
                    } else {
                        sqlString.append(StringUtils.format(
                                " {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
                                deptAlias, user.getDeptId(), user.getDeptId()));
                    }
                } else if (DataScopeConstants.DATA_SCOPE_SELF.equals(dataScope)) {
                    if (StringUtils.isNotBlank(userAlias)) {
                        String userFiled = whereType.filed();
                        String u = "user_id";
                        if (StringUtils.isNotEmpty(userFiled)) {
                            u = userFiled;
                        }
                        if ("main_process_order.".equals(topTableNameVal)) {
                            sqlString.append(StringUtils.format("{}." + u + " = {} " +
                                    "OR {}factory_id in (select factory_id from fm_factory_user where " + u + " =  {} and is_del = 0)", userAlias, user.getUserId(), topTableNameVal, user.getUserId()));
                        } else {
                            sqlString.append(StringUtils.format("{}." + u + " = {} ", userAlias, user.getUserId()));
                        }
                    } else {
                        // 数据权限为仅本人且没有userAlias别名不查询任何数据
                        sqlString.append(" 1=0 ");
                    }
                }
            }

            if (StringUtils.isNotBlank(sqlString)) {
                String dataScopeSql = sqlString.toString();
                wrapper.and(queryWrapper -> queryWrapper.apply(dataScopeSql));
            }
        } else {
            handleField(whereType, field, topTableNameVal, val, wrapper);
        }
    }

    private static <T> void handleField(WhereType whereType, Field field, String topTableNameVal, Object val, QueryWrapper<T> wrapper) {
        String colName = getFieldName(whereType, field, topTableNameVal);
        if (ObjectUtils.isNotEmpty(val)) {
            if (whereType != null && StringUtils.isNotEmpty(whereType.func())) {
                if (whereType.isHaving()) {
                    wrapper.having(whereType.func());
                } else {
                    String funcName = whereType.func();
                    funcName = funcName.replaceAll("\\{2}", colName);
                    int valCount = StringUtils.countMatches(funcName, "{1}");
                    if (valCount > 0) {
                        //走转换逻辑
                        if (val instanceof Boolean && whereType.func_value_conversion().length == 2) {
                            if ((boolean) val) {
                                val = whereType.func_value_conversion()[0];
                            } else {
                                val = whereType.func_value_conversion()[1];
                            }
                            funcName = funcName.replace("{1}", String.valueOf(val));
                            wrapper.apply(funcName);
                        }else if (val instanceof String[] && whereType.func_field_value().length > 0) {
                           String [] vs = (String[]) val;
                            for (int i = 0; i < vs.length; i++) {
                                if (i==0) {
                                    funcName = funcName.replace("{" + (i + 1) + "}", vs[i]);
                                }else {
                                    funcName = funcName.replace("{" + (i + Integer.parseInt(whereType.func_field_value()[0])) + "}", vs[i]);
                                }

                            }
                           wrapper.apply(funcName);
                        }else {
                            Object[] values = new Object[valCount];
                            for (int i = 0; i < valCount; i++) {
                                funcName = funcName.replace("{1}", "{" + i + "}");
                                values[i] = val;
                            }
                            wrapper.apply(funcName, values);
                        }
                    } else {
                        wrapper.apply(funcName);
                    }
                }
            } else {
                //没有注解，取默认为下划线拼接
                if (whereType == null) {
                    // 执行方法
                    typeFunc.get(WhereTypeEnum.EQ).whereFunc(wrapper, colName, val);
                } else {
                    if (whereType.ignore()) {
                        return;
                    } else {
                        if (whereType.fields().length == 0) {
                            if (StringUtils.isNotBlank(whereType.jsonKey())) {
                                colName = String.format("%s->>'$.%s'", colName, whereType.jsonKey());
                            }
                            typeFunc.get(whereType.type()).whereFunc(wrapper, colName, val);
                        } else {
                            //一个属性对应多个条件
                            Object finalVal = val;
                            wrapper.and(q -> {
                                for (String s : whereType.fields()) {
                                    typeFunc.get(whereType.type()).whereFunc(q.or(), s, finalVal);
                                }
                            });
                        }
                    }
                }
            }
        }
        if (whereType != null) {
            //分组
            if (whereType.groupBy()) {
                wrapper.groupBy(colName);
            }
            //排序
            if (whereType.sort()) {
                if (whereType.sortType().equals(WhereTypeEnum.DESC)) {
                    wrapper.orderByDesc(colName);
                } else if (whereType.sortType().equals(WhereTypeEnum.ASC)) {
                    wrapper.orderByAsc(colName);
                }
            }
        }
    }

    private static String getFieldName(WhereType whereType, Field field, String topTableNameVal) {
        String colName;
        String tableName;
        if (whereType == null || StringUtils.isBlank(whereType.filed())) {
            colName = camelToUnderline(field.getName());
        } else {
            colName = whereType.filed();
        }
        if (whereType == null || StringUtils.isBlank(whereType.tableName())) {
            tableName = topTableNameVal;
        } else {
            tableName = whereType.tableName();
        }
        if (!StringUtils.isBlank(tableName) && !tableName.contains(".")) {
            tableName = tableName + '.';
        }
        if (whereType != null && whereType.dateFormat()) {
            if(StrUtil.isNotBlank(tableName)){
                return "date_format (" + tableName+colName + ",'%Y-%m-%d')";
            }else{
                return "date_format (" + colName + ",'%Y-%m-%d')";
            }
        }
        return tableName + colName;
    }

    /**
     * 驼峰转下划线
     */
    public static String camelToUnderline(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append("_");
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

}
